package com.marketing.auth.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * JWT工具类
 */
@Component
public class JwtUtil {

    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);

    @Value("${jwt.secret:marketing-system-jwt-secret-key-2025}")
    private String jwtSecret;

    @Value("${jwt.expiration:86400000}")  // 24小时
    private Long jwtExpiration;

    /**
     * 生成JWT Token（基础版本，保持向后兼容）
     */
    public String generateToken(String username, Long userId) {
        logger.debug("开始生成JWT Token，用户名: {}, 用户ID: {}", username, userId);
        
        try {
            Map<String, Object> claims = new HashMap<>();
            claims.put("userId", userId);
            claims.put("username", username);
            
            String token = createToken(claims, username);
            logger.debug("JWT Token生成成功，用户名: {}, Token长度: {}", username, token.length());
            
            return token;
        } catch (Exception e) {
            logger.error("JWT Token生成失败，用户名: {}, 用户ID: {}, 异常: {}", username, userId, e.getMessage(), e);
            throw new RuntimeException("Token生成失败", e);
        }
    }
    
    /**
     * 生成包含权限信息的JWT Token
     */
    public String generateTokenWithPermissions(String username, Long userId, String role, 
                                               List<String> roleCodes, Set<String> permissionCodes) {
        logger.debug("开始生成包含权限的JWT Token，用户名: {}, 用户ID: {}, 角色: {}, 权限数量: {}", 
                username, userId, role, permissionCodes != null ? permissionCodes.size() : 0);
        
        try {
            Map<String, Object> claims = new HashMap<>();
            claims.put("userId", userId);
            claims.put("username", username);
            claims.put("role", role); // 主要角色（向后兼容）
            
            if (roleCodes != null && !roleCodes.isEmpty()) {
                claims.put("roles", roleCodes); // 所有角色编码
            }
            
            if (permissionCodes != null && !permissionCodes.isEmpty()) {
                claims.put("permissions", permissionCodes); // 所有权限编码
            }
            
            String token = createToken(claims, username);
            logger.debug("包含权限的JWT Token生成成功，用户名: {}, Token长度: {}", username, token.length());
            
            return token;
        } catch (Exception e) {
            logger.error("包含权限的JWT Token生成失败，用户名: {}, 用户ID: {}, 异常: {}", username, userId, e.getMessage(), e);
            throw new RuntimeException("Token生成失败", e);
        }
    }

    /**
     * 创建Token
     */
    private String createToken(Map<String, Object> claims, String subject) {
        logger.debug("创建JWT Token，subject: {}", subject);
        
        try {
            Date now = new Date();
            Date expiryDate = new Date(now.getTime() + jwtExpiration);
            
            logger.debug("Token时间信息 - 创建时间: {}, 过期时间: {}, 有效期: {}毫秒", now, expiryDate, jwtExpiration);

            String token = Jwts.builder()
                    .setClaims(claims)
                    .setSubject(subject)
                    .setIssuedAt(now)
                    .setExpiration(expiryDate)
                    .signWith(getSigningKey(), SignatureAlgorithm.HS512)
                    .compact();
                    
            logger.debug("JWT Token创建成功，subject: {}", subject);
            return token;
        } catch (Exception e) {
            logger.error("JWT Token创建失败，subject: {}, 异常: {}", subject, e.getMessage(), e);
            throw new RuntimeException("Token创建失败", e);
        }
    }

    /**
     * 从Token中获取用户名
     */
    public String getUsernameFromToken(String token) {
        logger.debug("从Token中获取用户名");
        
        try {
            Claims claims = getClaimsFromToken(token);
            String username = claims.getSubject();
            logger.debug("从Token获取用户名成功: {}", username);
            return username;
        } catch (Exception e) {
            logger.warn("从Token获取用户名失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 从Token中获取用户ID
     */
    public Long getUserIdFromToken(String token) {
        logger.debug("从Token中获取用户ID");
        
        try {
            Claims claims = getClaimsFromToken(token);
            Long userId = claims.get("userId", Long.class);
            logger.debug("从Token获取用户ID成功: {}", userId);
            return userId;
        } catch (Exception e) {
            logger.warn("从Token获取用户ID失败: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 从Token中获取主要角色
     */
    public String getRoleFromToken(String token) {
        logger.debug("从Token中获取主要角色");
        
        try {
            Claims claims = getClaimsFromToken(token);
            String role = claims.get("role", String.class);
            logger.debug("从Token获取主要角色成功: {}", role);
            return role;
        } catch (Exception e) {
            logger.warn("从Token获取主要角色失败: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 从Token中获取所有角色编码
     */
    @SuppressWarnings("unchecked")
    public List<String> getRoleCodesFromToken(String token) {
        logger.debug("从Token中获取所有角色编码");
        
        try {
            Claims claims = getClaimsFromToken(token);
            List<String> roleCodes = (List<String>) claims.get("roles");
            logger.debug("从Token获取角色编码成功，数量: {}", roleCodes != null ? roleCodes.size() : 0);
            return roleCodes;
        } catch (Exception e) {
            logger.warn("从Token获取角色编码失败: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 从Token中获取所有权限编码
     */
    @SuppressWarnings("unchecked")
    public Set<String> getPermissionCodesFromToken(String token) {
        logger.debug("从Token中获取所有权限编码");
        
        try {
            Claims claims = getClaimsFromToken(token);
            List<String> permissionList = (List<String>) claims.get("permissions");
            if (permissionList != null) {
                Set<String> permissionCodes = Set.copyOf(permissionList);
                logger.debug("从Token获取权限编码成功，数量: {}", permissionCodes.size());
                return permissionCodes;
            }
            return null;
        } catch (Exception e) {
            logger.warn("从Token获取权限编码失败: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 检查Token中是否包含指定权限
     */
    public boolean hasPermission(String token, String permissionCode) {
        logger.debug("检查Token中是否包含权限: {}", permissionCode);
        
        try {
            Set<String> permissions = getPermissionCodesFromToken(token);
            boolean hasPermission = permissions != null && permissions.contains(permissionCode);
            logger.debug("权限检查结果，权限: {}, 拥有: {}", permissionCode, hasPermission);
            return hasPermission;
        } catch (Exception e) {
            logger.warn("权限检查失败，权限: {}, 异常: {}", permissionCode, e.getMessage());
            return false;
        }
    }
    
    /**
     * 检查Token中是否包含指定角色
     */
    public boolean hasRole(String token, String roleCode) {
        logger.debug("检查Token中是否包含角色: {}", roleCode);
        
        try {
            // 检查主要角色
            String mainRole = getRoleFromToken(token);
            if (roleCode.equals(mainRole)) {
                logger.debug("在主要角色中找到匹配，角色: {}", roleCode);
                return true;
            }
            
            // 检查所有角色列表
            List<String> roles = getRoleCodesFromToken(token);
            boolean hasRole = roles != null && roles.contains(roleCode);
            logger.debug("角色检查结果，角色: {}, 拥有: {}", roleCode, hasRole);
            return hasRole;
        } catch (Exception e) {
            logger.warn("角色检查失败，角色: {}, 异常: {}", roleCode, e.getMessage());
            return false;
        }
    }

    /**
     * 从Token中获取过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            return claims.getExpiration();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 验证Token是否有效
     */
    public Boolean validateToken(String token, String username) {
        logger.debug("验证Token有效性，期望用户名: {}", username);
        
        try {
            final String tokenUsername = getUsernameFromToken(token);
            boolean isValid = (tokenUsername.equals(username) && !isTokenExpired(token));
            
            if (isValid) {
                logger.debug("Token验证成功，用户名: {}", username);
            } else {
                logger.warn("Token验证失败，期望用户名: {}, Token中的用户名: {}, 是否过期: {}", 
                        username, tokenUsername, isTokenExpired(token));
            }
            
            return isValid;
        } catch (Exception e) {
            logger.error("Token验证过程中发生异常，用户名: {}, 异常: {}", username, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 检查Token是否过期
     */
    public Boolean isTokenExpired(String token) {
        logger.debug("检查Token是否过期");
        
        try {
            final Date expiration = getExpirationDateFromToken(token);
            Date now = new Date();
            boolean expired = expiration.before(now);
            
            if (expired) {
                logger.debug("Token已过期，过期时间: {}, 当前时间: {}", expiration, now);
            } else {
                logger.debug("Token未过期，过期时间: {}, 当前时间: {}", expiration, now);
            }
            
            return expired;
        } catch (Exception e) {
            logger.warn("Token过期检查失败，默认为已过期: {}", e.getMessage());
            return true;
        }
    }

    /**
     * 从Token中获取Claims
     */
    private Claims getClaimsFromToken(String token) {
        logger.debug("从Token中解析Claims");
        
        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(getSigningKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
                    
            logger.debug("Token Claims解析成功，subject: {}, 过期时间: {}", claims.getSubject(), claims.getExpiration());
            return claims;
        } catch (ExpiredJwtException e) {
            logger.warn("Token已过期: {}", e.getMessage());
            throw e;
        } catch (MalformedJwtException e) {
            logger.warn("Token格式错误: {}", e.getMessage());
            throw e;
        } catch (SignatureException e) {
            logger.warn("Token签名验证失败: {}", e.getMessage());
            throw e;
        } catch (Exception e) {
            logger.error("Token解析失败: {}", e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 获取签名密钥
     */
    private SecretKey getSigningKey() {
        byte[] keyBytes = jwtSecret.getBytes();
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 获取Token过期时间（毫秒）
     */
    public Long getExpirationTime() {
        return jwtExpiration;
    }

    /**
     * 获取Token过期时间（秒）
     */
    public Long getExpirationTimeInSeconds() {
        return jwtExpiration / 1000;
    }
    
    /**
     * 解析Token获取所有声明信息（用于调试）
     */
    public Map<String, Object> getAllClaimsFromToken(String token) {
        logger.debug("解析Token获取所有声明信息");
        
        try {
            Claims claims = getClaimsFromToken(token);
            Map<String, Object> claimsMap = new HashMap<>();
            claimsMap.put("subject", claims.getSubject());
            claimsMap.put("issuedAt", claims.getIssuedAt());
            claimsMap.put("expiration", claims.getExpiration());
            claimsMap.put("userId", claims.get("userId"));
            claimsMap.put("username", claims.get("username"));
            claimsMap.put("role", claims.get("role"));
            claimsMap.put("roles", claims.get("roles"));
            claimsMap.put("permissions", claims.get("permissions"));
            
            logger.debug("Token声明信息解析成功");
            return claimsMap;
        } catch (Exception e) {
            logger.warn("Token声明信息解析失败: {}", e.getMessage());
            return null;
        }
    }
}